package org.example.myframework.frame.common.redis.utils;

import org.apache.commons.lang.StringUtils;
import org.example.myframework.frame.common.exception.FrameException;
import org.example.myframework.frame.common.exception.enums.FrameExceptionEnum;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@Component
public class RedisShareLockUtil {

    @Resource
    private RedisUtil redisUtil;

    private Long TIME_OUT = 10 * 1000L;

    /**
     * 加锁
     * @param lockKey
     * @param requestId 区别两个业务之间的锁是否是同一个锁，防止其他的业务将当前线程的锁释放
     * @param timeout
     * @return
     */
    public boolean lock(String lockKey, String requestId, Long timeout) {
        // 参数校验
        if (StringUtils.isBlank(lockKey) || StringUtils.isBlank(requestId) || timeout <= 0 ) {
            throw new FrameException(FrameExceptionEnum.LOCK_DATA_ERROR);
        }
        long currentTimeMillis = System.currentTimeMillis();
        // 过期时间
        long timeOutMillis = currentTimeMillis + TIME_OUT;
        Boolean lockResult = false;
        while (currentTimeMillis < timeOutMillis) {
            // 通过setnx进行加锁设置，setnx用于保证原子性，如果先set在expire，可能会有高并发的问题
            lockResult = redisUtil.setNx(lockKey, requestId, timeout, TimeUnit.MILLISECONDS);
            if (lockResult) {
                return lockResult;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            // 重置休眠后的时间
            currentTimeMillis = System.currentTimeMillis();
        }
        // 加锁锁自旋
        // 借助setnx进行加锁
        return lockResult;
    }

    /**
     * 解锁
     * @param lockKey
     * @param requestId
     * @return
     */
    public boolean unLock(String lockKey, String requestId) {
        if (StringUtils.isBlank(lockKey) || StringUtils.isBlank(requestId)) {
            throw new FrameException(FrameExceptionEnum.UNLOCK_DATA_ERROR);
        }
        Boolean result = false;
        try {
            String value = redisUtil.get(lockKey).toString();
            if (requestId.equals(value)) {
                result = redisUtil.delete(lockKey);
                return result;
            }
        } catch (Exception e) {

        }
        return result;
    }

    /**
     * 加锁，无需自旋
     * @param lockKey
     * @param requestId
     * @param timeout
     * @return
     */
    public boolean tryLock(String lockKey, String requestId, Long timeout) {
        // 参数校验
        if (StringUtils.isBlank(lockKey) || StringUtils.isBlank(requestId) || timeout <= 0 ) {
            throw new FrameException(FrameExceptionEnum.LOCK_DATA_ERROR);
        }
        Boolean lockResult = false;
        lockResult = redisUtil.setNx(lockKey, requestId, timeout, TimeUnit.MILLISECONDS);
        if (lockResult) {
            return lockResult;
        }
        return lockResult;
    }

}
